package com.wangzhen.spring.ioc.lifecycle.instantiation;

import com.wangzhen.spring.ioc.dependcy.entity.SuperUser;
import com.wangzhen.spring.ioc.dependcy.entity.Terminate;
import com.wangzhen.spring.ioc.dependcy.entity.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;

/**
 * 自定义InstantiationAwareBeanPostProcessor，演示对象实例化前、实例化后
 */
public class InstantiationAwareBeanPostProcessorDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
        // 方法一：增加自定义的InstantiationAwareBeanPostProcessor
        context.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
        context.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
        // 方法二：也可以直接在XML中定义该bean

        // 加载spring-ioc moudle中定义的bean
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:/META-INF/dependcy-lookup-context.xml");
        //
        context.preInstantiateSingletons();

        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(SuperUser.class));
        Terminate bean = context.getBean(Terminate.class);

        context.destroyBean("terminate", bean);
    }


    static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
        /**
         * 如果返回的对象不为null：那么后续的流程就短路不再执行了(不会populateBean，不会InitializingBean)
         * @param beanClass
         * @param beanName
         * @return
         * @throws BeansException
         */
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (User.class == beanClass && "user".equals(beanName)) {
                return new User();
            }
            return null;
        }

        /**
         * 如果返回值为false，就不会执行populateBean了，不会为bean设置属性了，但是会执行bean的InitialBean流程
         * 如果返回值为true， 那么bean的属性值会被设置
         * 对象已经实例化，但是bean对象的字段没有赋值
         * @param bean
         * @param beanName
         * @return
         * @throws BeansException
         */
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            if (bean.getClass() == SuperUser.class && "superUser".equals(beanName))  {
                User user = User.class.cast(bean);
                user.setName("wangzhen");
                user.setAge(900);
                return false;
            }
            return true;
        }

        /**
         * 1、这个方法对于AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor来说是注入@Resource和@Autowired字段
         * 2、在给定的属性应用到bean对象之前的后置操作，提供了修改PropertiesValues的机会
         * @param pvs
         * @param bean
         * @param beanName
         * @return
         * @throws BeansException
         */
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if ("terminate".equals(bean) || Terminate.class == bean.getClass()) {
                MutablePropertyValues values = null;
                if (pvs instanceof MutablePropertyValues) {
                    values = MutablePropertyValues.class.cast(pvs);
                } else {
                    values = new MutablePropertyValues();
                }
                // 因为MutablePropertyValues--> PropertyValue 是不可变的，所以如果要修改已经存在的字段值，需要先删除然后在新增
                if (values.contains("whoAmI")) {
                    values.removePropertyValue("whoAmI");
                    values.addPropertyValue("whoAmI", "postProcessProperties() method changed whoAmI value");
                    System.out.println("postProcessProperties() method changed whoAmI value");
                    return values;
                }
            }
            return null;
        }
    }

}
